package com.executor.gateway.filters.tracing;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.UUID;

/**
 * 链路追踪全局过滤器
 * 为每个请求生成或传递追踪ID，用于分布式链路追踪
 * 
 * @author miaoguoxin
 * @date 2024/01/01
 */
@Component
@Slf4j
public class TraceIdGlobalFilter implements GlobalFilter, Ordered {

    private static final String TRACE_ID_HEADER = "X-Trace-Id";
    private static final String SPAN_ID_HEADER = "X-Span-Id";
    private static final String PARENT_SPAN_ID_HEADER = "X-Parent-Span-Id";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        
        // 获取或生成追踪ID
        String traceId = getOrGenerateTraceId(request);
        String spanId = generateSpanId();
        String parentSpanId = request.getHeaders().getFirst(SPAN_ID_HEADER);

        // 将追踪信息添加到请求头
        ServerHttpRequest mutatedRequest = request.mutate()
                .header(TRACE_ID_HEADER, traceId)
                .header(SPAN_ID_HEADER, spanId)
                .header(PARENT_SPAN_ID_HEADER, parentSpanId != null ? parentSpanId : "")
                .build();

        // 将追踪信息存储到exchange attributes中
        exchange.getAttributes().put(TRACE_ID_HEADER, traceId);
        exchange.getAttributes().put(SPAN_ID_HEADER, spanId);
        exchange.getAttributes().put(PARENT_SPAN_ID_HEADER, parentSpanId);

        // 记录请求开始日志
        logRequestStart(mutatedRequest, traceId, spanId);

        return chain.filter(exchange.mutate().request(mutatedRequest).build())
                .doOnSuccess(aVoid -> logRequestSuccess(mutatedRequest, traceId, spanId))
                .doOnError(throwable -> logRequestError(mutatedRequest, traceId, spanId, throwable))
                .doFinally(signalType -> logRequestComplete(mutatedRequest, traceId, spanId, signalType));
    }

    /**
     * 获取或生成追踪ID
     */
    private String getOrGenerateTraceId(ServerHttpRequest request) {
        String traceId = request.getHeaders().getFirst(TRACE_ID_HEADER);
        if (!StringUtils.hasText(traceId)) {
            traceId = generateTraceId();
        }
        return traceId;
    }

    /**
     * 生成追踪ID
     */
    private String generateTraceId() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 生成跨度ID
     */
    private String generateSpanId() {
        return UUID.randomUUID().toString().replace("-", "").substring(0, 16);
    }

    /**
     * 记录请求开始日志
     */
    private void logRequestStart(ServerHttpRequest request, String traceId, String spanId) {
        log.info("Request started - TraceId: {}, SpanId: {}, Method: {}, URI: {}, RemoteAddress: {}",
                traceId, spanId, request.getMethod(), request.getURI(), 
                request.getRemoteAddress() != null ? request.getRemoteAddress().getAddress() : "unknown");
    }

    /**
     * 记录请求成功日志
     */
    private void logRequestSuccess(ServerHttpRequest request, String traceId, String spanId) {
        log.info("Request completed successfully - TraceId: {}, SpanId: {}, URI: {}",
                traceId, spanId, request.getURI());
    }

    /**
     * 记录请求错误日志
     */
    private void logRequestError(ServerHttpRequest request, String traceId, String spanId, Throwable throwable) {
        log.error("Request failed - TraceId: {}, SpanId: {}, URI: {}, Error: {}",
                traceId, spanId, request.getURI(), throwable.getMessage(), throwable);
    }

    /**
     * 记录请求完成日志
     */
    private void logRequestComplete(ServerHttpRequest request, String traceId, String spanId, reactor.core.publisher.SignalType signalType) {
        log.debug("Request finished - TraceId: {}, SpanId: {}, URI: {}, Signal: {}",
                traceId, spanId, request.getURI(), signalType);
    }

    @Override
    public int getOrder() {
        // 最高优先级，确保追踪信息在所有其他过滤器之前设置
        return Ordered.HIGHEST_PRECEDENCE;
    }
}